gl renderer: Add a clip stack
authorTimm Bäder <mail@baedert.org>
Sun, 30 Dec 2018 14:25:01 +0000 (15:25 +0100)
committerTimm Bäder <mail@baedert.org>
Mon, 31 Dec 2018 11:44:02 +0000 (12:44 +0100)
So we can check that the currently set clip is the first one and now
intersect with it. This first clip is always the entire viewport or the
entire render_area and we don't want to end up drawing things to a
texture because of it.

gsk/gl/gskglrenderer.c
gsk/gl/gskglrenderops.c
gsk/gl/gskglrenderopsprivate.h

index ff66e803e98243b3d25414cc87a8dd62ca48d6b1..da97872828c3572b725b28a26ebef921d58b2af6 100644 (file)
@@ -895,7 +895,6 @@ render_clip_node (GskGLRenderer   *self,
                   GskRenderNode   *node,
                   RenderOpBuilder *builder)
 {
-  GskRoundedRect prev_clip;
   GskRenderNode *child = gsk_clip_node_get_child (node);
   graphene_rect_t transformed_clip;
   graphene_rect_t intersection;
@@ -905,16 +904,41 @@ render_clip_node (GskGLRenderer   *self,
   ops_transform_bounds_modelview (builder, &transformed_clip, &transformed_clip);
 
   graphene_rect_intersection (&transformed_clip,
-                              &builder->current_clip.bounds,
+                              &builder->current_clip->bounds,
                               &intersection);
 
   gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f);
 
-  prev_clip = ops_set_clip (builder, &child_clip);
+  ops_push_clip (builder, &child_clip);
   gsk_gl_renderer_add_render_ops (self, child, builder);
-  ops_set_clip (builder, &prev_clip);
+  ops_pop_clip (builder);
 }
 
+static gboolean
+gsk_rounded_rect_intersection (const GskRoundedRect *self,
+                               const GskRoundedRect *other,
+                               GskRoundedRect       *out_intersection)
+{
+  const graphene_rect_t *self_bounds = &self->bounds;
+  const graphene_rect_t *other_bounds = &other->bounds;
+
+  if (graphene_rect_contains_rect (self_bounds, other_bounds))
+    {
+      *out_intersection = *other;
+      return TRUE;
+    }
+
+  /* TODO: There are a few cases here that we can express using a single
+   *       rounded rectangle, which are even interesting in every day usage.
+   *       For example, a partially scrolled-away rounded rectangle
+   *       might just work.
+   */
+
+  return FALSE;
+}
+
+
+
 static inline void
 render_rounded_clip_node (GskGLRenderer       *self,
                           GskRenderNode       *node,
@@ -923,31 +947,41 @@ render_rounded_clip_node (GskGLRenderer       *self,
   const float scale = ops_get_scale (builder);
   GskRoundedRect child_clip = *gsk_rounded_clip_node_peek_clip (node);
   GskRoundedRect transformed_clip;
-  GskRoundedRect prev_clip;
   GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
+  GskRoundedRect intersection;
+  gboolean need_offscreen;
   int i;
 
   transformed_clip = child_clip;
   ops_transform_bounds_modelview (builder, &child_clip.bounds, &transformed_clip.bounds);
 
-  if (graphene_rect_contains_rect (&builder->current_clip.bounds,
-                                   &transformed_clip.bounds))
+  if (!ops_has_clip (builder))
+    {
+      intersection = transformed_clip;
+      need_offscreen = FALSE;
+    }
+  else
+    {
+      need_offscreen = !gsk_rounded_rect_intersection (builder->current_clip,
+                                                       &transformed_clip,
+                                                       &intersection);
+    }
+
+  if (!need_offscreen)
     {
       /* If they don't intersect at all, we can simply set
        * the new clip and add the render ops */
       for (i = 0; i < 4; i ++)
         {
-          transformed_clip.corner[i].width *= scale;
-          transformed_clip.corner[i].height *= scale;
+          intersection.corner[i].width *= scale;
+          intersection.corner[i].height *= scale;
         }
 
-      prev_clip = ops_set_clip (builder, &transformed_clip);
+      ops_push_clip (builder, &intersection);
       gsk_gl_renderer_add_render_ops (self, child, builder);
-
-      ops_set_clip (builder, &prev_clip);
+      ops_pop_clip (builder);
     }
-  else if (graphene_rect_intersection (&builder->current_clip.bounds,
-                                       &transformed_clip.bounds, NULL))
+  else
     {
       const float min_x = builder->dx + node->bounds.origin.x;
       const float min_y = builder->dy + node->bounds.origin.y;
@@ -973,12 +1007,12 @@ render_rounded_clip_node (GskGLRenderer       *self,
           child_clip.corner[i].height *= scale;
         }
 
-      prev_clip = ops_set_clip (builder, &child_clip);
+      ops_push_clip (builder, &child_clip);
       add_offscreen_ops (self, builder, &node->bounds,
                          child,
                          &texture_id, &is_offscreen, TRUE, FALSE);
+      ops_pop_clip (builder);
 
-      ops_set_clip (builder, &prev_clip);
       ops_set_program (builder, &self->blit_program);
       ops_set_texture (builder, texture_id);
 
@@ -1205,7 +1239,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
       int texture_id, render_target;
       int blurred_render_target;
       int prev_render_target;
-      GskRoundedRect prev_clip, blit_clip;
+      GskRoundedRect blit_clip;
 
       texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
       gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
@@ -1228,7 +1262,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
 
       /* Draw outline */
       ops_set_program (builder, &self->color_program);
-      prev_clip = ops_set_clip (builder, &offset_outline);
+      ops_push_clip (builder, &offset_outline);
       ops_set_color (builder, gsk_outset_shadow_node_peek_color (node));
       ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
         { { 0,                            }, { 0, 1 }, },
@@ -1246,6 +1280,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
       blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE);
 
       ops_set_render_target (builder, blurred_render_target);
+      ops_pop_clip (builder);
       op.op = OP_CLEAR;
       ops_add (builder, &op);
 
@@ -1259,7 +1294,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
       op.blur.radius = blur_radius;
       ops_add (builder, &op);
 
-      ops_set_clip (builder, &blit_clip);
+      ops_push_clip (builder, &blit_clip);
       ops_set_texture (builder, texture_id);
       ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
         { { 0,             0              }, { 0, 1 }, },
@@ -1272,7 +1307,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
       });
 
 
-      ops_set_clip (builder, &prev_clip);
+      ops_pop_clip (builder);
       ops_set_viewport (builder, &prev_viewport);
       ops_pop_modelview (builder);
       ops_set_projection (builder, &prev_projection);
@@ -2254,7 +2289,7 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
                                     &node->bounds,
                                     &transformed_node_bounds);
 
-    if (!graphene_rect_intersection (&builder->current_clip.bounds,
+    if (!graphene_rect_intersection (&builder->current_clip->bounds,
                                      &transformed_node_bounds, NULL))
       return;
   }
@@ -2384,7 +2419,6 @@ add_offscreen_ops (GskGLRenderer         *self,
   graphene_matrix_t prev_projection;
   graphene_rect_t prev_viewport;
   graphene_matrix_t item_proj;
-  GskRoundedRect prev_clip;
   float prev_opacity;
   int texture_id = 0;
 
@@ -2444,10 +2478,10 @@ add_offscreen_ops (GskGLRenderer         *self,
                                                          bounds->origin.y * scale,
                                                          width, height));
   if (reset_clip)
-    prev_clip = ops_set_clip (builder,
-                              &GSK_ROUNDED_RECT_INIT (bounds->origin.x * scale,
-                                                      bounds->origin.y * scale,
-                                                      width, height));
+    ops_push_clip (builder,
+                   &GSK_ROUNDED_RECT_INIT (bounds->origin.x * scale,
+                                           bounds->origin.y * scale,
+                                           width, height));
 
   builder->dx = 0;
   builder->dy = 0;
@@ -2460,7 +2494,7 @@ add_offscreen_ops (GskGLRenderer         *self,
   builder->dy = dy;
 
   if (reset_clip)
-    ops_set_clip (builder, &prev_clip);
+    ops_pop_clip (builder);
 
   ops_set_viewport (builder, &prev_viewport);
   ops_pop_modelview (builder);
@@ -2696,25 +2730,34 @@ gsk_gl_renderer_do_render (GskRenderer           *renderer,
   render_op_builder.current_opacity = 1.0f;
   render_op_builder.render_ops = self->render_ops;
   ops_push_modelview (&render_op_builder, &modelview);
+  cairo_rectangle_int_t render_extents;
+
+  /*cairo_region_get_extents (self->render_region, &render_extents);*/
   /* Initial clip is self->render_region! */
   if (self->render_region != NULL)
     {
-      cairo_rectangle_int_t render_extents;
+      GskRoundedRect transformed_render_region = { 0, };
+      /*cairo_rectangle_int_t render_extents;*/
 
       cairo_region_get_extents (self->render_region, &render_extents);
-      render_op_builder.current_clip = GSK_ROUNDED_RECT_INIT (render_extents.x,
-                                                              render_extents.y,
-                                                              render_extents.width,
-                                                              render_extents.height);
 
       ops_transform_bounds_modelview (&render_op_builder,
-                                      &render_op_builder.current_clip.bounds,
-                                      &render_op_builder.current_clip.bounds);
+                                      &GRAPHENE_RECT_INIT (render_extents.x,
+                                                           render_extents.y,
+                                                           render_extents.width,
+                                                           render_extents.height),
+                                      &transformed_render_region.bounds);
+      ops_push_clip (&render_op_builder, &transformed_render_region);
     }
   else
     {
-      gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, viewport, 0.0f);
+      ops_push_clip (&render_op_builder,
+                     &GSK_ROUNDED_RECT_INIT (viewport->origin.x,
+                                             viewport->origin.y,
+                                             viewport->size.width,
+                                             viewport->size.height));
     }
+  /*gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, viewport, 0.0f);*/
 
 
   if (fbo_id != 0)
@@ -2722,9 +2765,18 @@ gsk_gl_renderer_do_render (GskRenderer           *renderer,
 
   gsk_gl_renderer_add_render_ops (self, root, &render_op_builder);
 
+  /*if (self->render_region)*/
+  /*add_rect_outline_ops (self, &render_op_builder,*/
+                        /*&GRAPHENE_RECT_INIT (*/
+                                             /*render_extents.x,*/
+                                             /*render_extents.y,*/
+                                             /*render_extents.width,*/
+                                             /*render_extents.height));*/
+
   /* We correctly reset the state everywhere */
   g_assert_cmpint (render_op_builder.current_render_target, ==, fbo_id);
   ops_pop_modelview (&render_op_builder);
+  ops_pop_clip (&render_op_builder);
   ops_finish (&render_op_builder);
 
   /*g_message ("Ops: %u", self->render_ops->len);*/
index db77ea7c68d08a0bd473ce85febacd76d99a405b..86b44e06e2170eb9ddf04f169dd142bf91cac0c6 100644 (file)
@@ -12,6 +12,9 @@ ops_finish (RenderOpBuilder *builder)
 {
   if (builder->mv_stack)
     g_array_free (builder->mv_stack, TRUE);
+
+  if (builder->clip_stack)
+    g_array_free (builder->clip_stack, TRUE);
 }
 
 static inline void
@@ -208,9 +211,9 @@ ops_set_program (RenderOpBuilder *builder,
       memcmp (&builder->current_clip, &program_state->clip, sizeof (GskRoundedRect)) != 0)
     {
       op.op = OP_CHANGE_CLIP;
-      op.clip = builder->current_clip;
+      op.clip = *builder->current_clip;
       g_array_append_val (builder->render_ops, op);
-      program_state->clip = builder->current_clip;
+      program_state->clip = *builder->current_clip;
     }
 
   if (program_state->opacity != builder->current_opacity)
@@ -224,12 +227,11 @@ ops_set_program (RenderOpBuilder *builder,
   builder->current_program_state = &builder->program_state[program->index];
 }
 
-GskRoundedRect
+static void
 ops_set_clip (RenderOpBuilder      *builder,
               const GskRoundedRect *clip)
 {
   RenderOp *last_op;
-  GskRoundedRect prev_clip;
 
   if (builder->render_ops->len > 0)
     {
@@ -251,11 +253,49 @@ ops_set_clip (RenderOpBuilder      *builder,
 
   if (builder->current_program != NULL)
     builder->current_program_state->clip = *clip;
+}
+
+void
+ops_push_clip (RenderOpBuilder      *self,
+               const GskRoundedRect *clip)
+{
+  if (G_UNLIKELY (self->clip_stack == NULL))
+    self->clip_stack = g_array_new (FALSE, TRUE, sizeof (GskRoundedRect));
+
+  g_assert (self->clip_stack != NULL);
+
+  g_array_append_val (self->clip_stack, *clip);
+  self->current_clip = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
+  ops_set_clip (self, clip);
+}
+
+void
+ops_pop_clip (RenderOpBuilder *self)
+{
+  const GskRoundedRect *head;
+
+  g_assert (self->clip_stack);
+  g_assert (self->clip_stack->len >= 1);
 
-  prev_clip = builder->current_clip;
-  builder->current_clip = *clip;
+  self->clip_stack->len --;
+  head = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
 
-  return prev_clip;
+  if (self->clip_stack->len >= 1)
+    {
+      self->current_clip = head;
+      ops_set_clip (self, head);
+    }
+  else
+    {
+      self->current_clip = NULL;
+    }
+}
+
+gboolean
+ops_has_clip (RenderOpBuilder *self)
+{
+  return self->clip_stack != NULL &&
+         self->clip_stack->len > 1;
 }
 
 static void
@@ -301,7 +341,7 @@ ops_push_modelview (RenderOpBuilder         *builder,
   MatrixStackEntry *entry;
 
   if (G_UNLIKELY (builder->mv_stack == NULL))
-      builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
+    builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
 
   g_assert (builder->mv_stack != NULL);
 
index 1d1594172d3c1dec86b6f0946d08c606e022d985..67c50641e1becd3b162dad9800ab826cdbb61b63 100644 (file)
@@ -248,7 +248,6 @@ typedef struct
   const Program *current_program;
   int current_render_target;
   int current_texture;
-  GskRoundedRect current_clip;
 
   graphene_matrix_t current_projection;
   graphene_rect_t current_viewport;
@@ -264,6 +263,10 @@ typedef struct
   GArray *mv_stack;
   /* Pointer into mv_stack */
   const graphene_matrix_t *current_modelview;
+
+  /* Same thing */
+  GArray *clip_stack;
+  const GskRoundedRect *current_clip;
 } RenderOpBuilder;
 
 
@@ -282,8 +285,10 @@ float             ops_get_scale          (const RenderOpBuilder   *builder);
 void              ops_set_program        (RenderOpBuilder         *builder,
                                           const Program           *program);
 
-GskRoundedRect    ops_set_clip           (RenderOpBuilder         *builder,
+void              ops_push_clip          (RenderOpBuilder         *builder,
                                           const GskRoundedRect    *clip);
+void              ops_pop_clip           (RenderOpBuilder         *builder);
+gboolean          ops_has_clip           (RenderOpBuilder         *builder);
 
 void              ops_transform_bounds_modelview (const RenderOpBuilder *builder,
                                                   const graphene_rect_t *src,